package com.zndroid.base.ui.common;

import android.os.Bundle;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.viewbinding.ViewBinding;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;

import com.zndroid.base.callback.inner.mvp.IView;
import com.zndroid.base.exception.SupplyIllegalArgumentException;
import com.zndroid.base.exception.SupplyIsNullException;
import com.zndroid.base.ui.impl.BasePresenter;

/**
 * common ViewPage Fragment, impl by {@link ViewPager2}
 * @author lzy
 */
@SuppressWarnings("unused")
public abstract class CommonViewPageFragment<VB extends ViewBinding, P extends BasePresenter<? extends IView>> extends CommonFragment<VB, P> {
    private @Nullable ViewPager2 viewPager2;
    private Fragment[] fragments;
    private int index = onSupplyDefaultIndex();

    private final @NonNull ViewPager2.OnPageChangeCallback mInnerOnPageChangeCallback = new ViewPager2.OnPageChangeCallback() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            super.onPageScrolled(position, positionOffset, positionOffsetPixels);
            onBackPageScrolled(position, positionOffset, positionOffsetPixels);
        }

        @Override
        public void onPageSelected(int position) {
            index = position;
            super.onPageSelected(position);
            onBackPageSelected(position);
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            super.onPageScrollStateChanged(state);
            onBackPageScrollStateChanged(state);
        }
    };

    /**
     * supply {@link ViewPager2} as impl
     *
     * @return ViewPager2
     * */
    protected abstract ViewPager2 onSupplyViewPage2();

    /**
     * supply each {@link Fragment} instance
     *
     * @return Fragment[]
     * */
    protected abstract Fragment[] onSupplyFragments();

    /**
     * index when init show, default '0'
     *
     * @return index
     * */
    protected int onSupplyDefaultIndex() {return 0;}

    /**
     * Enable or disable user initiated scrolling. By default, user initiated scrolling is enabled.
     *
     * @return {@code true} to allow user initiated scrolling, {@code false} to block user initiated scrolling
     */
    protected boolean isSlideAble() {return true;}

    /**
     * get {@link ViewPager2} instance
     *
     * @return ViewPager2
     * */
    @Nullable
    public ViewPager2 currentViewPager2() {
        return viewPager2;
    }

    /**
     * get current showing fragment
     *
     * @return {@link Fragment}
     * */
    public Fragment currentFragment() {
        if (null != fragments && fragments.length > 0) {
            return fragments[index];
        } else {
            return null;
        }
    }

    /**
     * get current showing index
     *
     * @return index
     * */
    public int currentSelectedIndex() {
        return index;
    }

    /**
     * select index item
     *
     * @param index Item index to select
     * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately
     * */
    public void toSelectItem(int index, boolean smoothScroll) {
        assert viewPager2 != null;
        viewPager2.setCurrentItem(index, smoothScroll);
    }

    /**
     * (开启预加载，默认懒加载模式)
     * Set the number of pages that should be retained to either side of the currently visible page(s).
     * Pages beyond this limit will be recreated from the adapter when needed.
     *
     * @param limit How many pages will be kept offscreen on either side.
     *              Valid values are all values {@code >= 1} and {@link ViewPager2#OFFSCREEN_PAGE_LIMIT_DEFAULT}
     * */
    public void toEnablePreLoad(@ViewPager2.OffscreenPageLimit int limit) {
        assert viewPager2 != null;
        viewPager2.setOffscreenPageLimit(limit);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        if (checkParamIsOk()) {
            initViewPage();
        }
    }

    private boolean checkParamIsOk() {
        if (null == onSupplyViewPage2()) {
            throw new SupplyIsNullException("ViewPage2[#onSupplyViewPage2(...)]");
        }

        if (null == onSupplyFragments() || 0 == onSupplyFragments().length) {
            throw new SupplyIllegalArgumentException("fragments[#onSupplyFragments(...)]", "not null or empty.");
        }

        return true;
    }

    private void initViewPage() {
        viewPager2 = onSupplyViewPage2();
        fragments = onSupplyFragments();

        assert viewPager2 != null;
        assert fragments != null;

        viewPager2.registerOnPageChangeCallback(mInnerOnPageChangeCallback);
        viewPager2.setAdapter(new FragmentStateAdapter(this) {
            @NonNull
            @Override
            public Fragment createFragment(int position) {
                return fragments[position];
            }

            @Override
            public int getItemCount() {
                return fragments.length;
            }
        });

        //listener
        if (null != viewPager2.getAdapter() && viewPager2.getAdapter().getItemCount() <= 0) {
            viewPager2.unregisterOnPageChangeCallback(mInnerOnPageChangeCallback);
            viewPager2.registerOnPageChangeCallback(mInnerOnPageChangeCallback);
        }

        //是否可滑动
        viewPager2.setUserInputEnabled(isSlideAble());
        //懒加载模式(默认:ViewPage2自带懒加载效果)
//        viewPager2.setOffscreenPageLimit(-1);
    }

    @Override
    protected void doOnDestroy() {
        super.doOnDestroy();
        if (null != viewPager2) {
            viewPager2.unregisterOnPageChangeCallback(mInnerOnPageChangeCallback);
        }
    }

    /**
     * This method will be invoked when the current page is scrolled, either as part
     * of a programmatically initiated smooth scroll or a user initiated touch scroll.
     *
     * @param position Position index of the first page currently being displayed.
     *                 Page position+1 will be visible if positionOffset is nonzero.
     * @param positionOffset Value from [0, 1) indicating the offset from the page at position.
     * @param positionOffsetPixels Value in pixels indicating the offset from position.
     */
    protected void onBackPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    /**
     * page selected call back
     *
     * @param position index
     * */
    protected void onBackPageSelected(int position) {
    }

    /**
     * Called when the scroll state changes. Useful for discovering when the user begins
     * dragging, when a fake drag is started, when the pager is automatically settling to the
     * current page, or when it is fully stopped/idle. {@code state} can be one of {@link
     * ViewPager2#SCROLL_STATE_IDLE}, {@link ViewPager2#SCROLL_STATE_DRAGGING} or {@link ViewPager2#SCROLL_STATE_SETTLING}.
     */
    protected void onBackPageScrollStateChanged(int state) {
    }

}
